/*
*	Copyright(c) 2020 lutianming email：641471957@qq.com
*
*	Sheeps may be copied only under the terms of the GNU Affero General Public License v3.0
*/

#include "lsheeps.h"
#include "LuaProtocol.h"
#include "luavms.h"

#define CHECK_LOG_BUFF(size, offset) if(offset > size) return offset;

static int obj_tostring(lua_State* L, const char* split, int index, char* buf, int bufsize, int offset, std::map<const void*, bool> &table_ref) {
	switch (lua_type(L, index)) {
	case LUA_TNUMBER: {
		if (lua_isinteger(L, index))
			offset += snprintf(buf + offset, bufsize - offset, "%s%lld", split, lua_tointeger(L, index));
		else
			offset += snprintf(buf + offset, bufsize - offset, "%s%f", split, (double)lua_tonumber(L, index));
		CHECK_LOG_BUFF(bufsize, offset);
		break;
	}
	case LUA_TSTRING: {
		offset += snprintf(buf + offset, bufsize - offset, "%s", split);
		CHECK_LOG_BUFF(bufsize, offset);
		size_t len = 0;
		const char* ptr = lua_tolstring(L, index, &len);
		if (bufsize - offset > (int)len) {
			memcpy(buf + offset, ptr, len);
			offset += (int)len;
		}
		break;
	}
	case LUA_TBOOLEAN:
		offset += snprintf(buf + offset, bufsize - offset, "%s%s", split, lua_toboolean(L, index) ? "true" : "false");
		CHECK_LOG_BUFF(bufsize, offset);
		break;
	case LUA_TNIL:
		offset += snprintf(buf + offset, bufsize - offset, "%snil", split);
		CHECK_LOG_BUFF(bufsize, offset);
		break;
	case LUA_TTABLE: {
		offset += snprintf(buf + offset, bufsize - offset, "%s{", split);
		CHECK_LOG_BUFF(bufsize, offset);
		int top = lua_gettop(L);
		lua_pushnil(L);
		lua_copy(L, index, -1);

		const void* p = lua_topointer(L, -1);
		if (table_ref.find(p) == table_ref.end()) {
			table_ref.insert(std::make_pair(p, true));
			lua_pushnil(L);
			bool first = true;
			while (lua_next(L, -2) != 0) {
				if (first)
					first = false;
				else {
					offset += snprintf(buf + offset, bufsize - offset, ",");
					CHECK_LOG_BUFF(bufsize, offset);
				}
				offset = obj_tostring(L, "", top + 2, buf, bufsize, offset, table_ref);
				CHECK_LOG_BUFF(bufsize, offset);
				offset += snprintf(buf + offset, bufsize - offset, ":");
				CHECK_LOG_BUFF(bufsize, offset);
				offset = obj_tostring(L, "", top + 3, buf, bufsize, offset, table_ref);
				CHECK_LOG_BUFF(bufsize, offset);
				lua_pop(L, 1);
			}
		}
		else {
			offset += snprintf(buf + offset, bufsize - offset, "%stable[%p]", split, p);
			CHECK_LOG_BUFF(bufsize, offset);
		}

		lua_pop(L, 1);
		offset += snprintf(buf + offset, bufsize - offset, "}");
		CHECK_LOG_BUFF(bufsize, offset);
		break;
		}
	default: {
		int tt = luaL_getmetafield(L, index, "__name");  /* try name */
		const char* kind = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : luaL_typename(L, index);
		offset += snprintf(buf + offset, bufsize - offset, "%s%s[%p]", split, kind, lua_topointer(L, index));
		CHECK_LOG_BUFF(bufsize, offset);
		if (tt != LUA_TNIL)
			lua_remove(L, -2);  /* remove '__name' */
		break;
		}
	}
	return offset;
}

static int api_Log(lua_State* L)
{
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	luaL_checktype(L, 1, LUA_TNUMBER);
	int log_level = (uint8_t)lua_tonumber(L, 1);
	lua_pop(L, 1);
	int logfd = proto->Task->logfd;
	if (logfd < 0 || CheckLogLevel(logfd, log_level))
		return 0;
	char buf[MAX_LOG_LEN] = { 0x0 };
	int offset = 0;

	struct tm tmm;
	time_t now = NOWTIME;
#ifdef __WINDOWS__
	localtime_s(&tmm, &now);
#else
	localtime_r(&now, &tmm);
#endif // __WINDOWS__
	char* slog = GetLogStr(log_level);
	offset = snprintf(buf, MAX_LOG_LEN, "[%s:%lu]\t[NO.%d:%d][%02d-%02d %02d:%02d:%02d]", slog, (u_long)THREAD_ID, proto->UserNumber, proto->_fram_data.MsgPointer.index, tmm.tm_mon + 1, tmm.tm_mday, tmm.tm_hour, tmm.tm_min, tmm.tm_sec);

	int argc = lua_gettop(L);
	std::map<const void*, bool> table_ref;
	for (int i = 2; i <= argc; i++) {
		offset = obj_tostring(L, i >2 ?" ":"", i, buf, MAX_LOG_LEN -3, offset, table_ref);
		if ((size_t)offset >= MAX_LOG_LEN - 3) {
			offset = MAX_LOG_LEN - 4;
			break;
		}
	}
	offset += snprintf(buf + offset, (size_t)MAX_LOG_LEN - offset, "\r\n");
#ifdef __WINDOWS__
	HANDLE fd = GetLogFileHandle(logfd);
	WriteFile(fd, buf, offset, &_log_written, NULL);
#else
	write(GetLogFileHandle(logfd), buf, offset);
#endif // __WINDOWS__
	if (log_stdout) printf("%.*s", offset, buf);
	return 0;
}

static int api_Microsecond(lua_State* L) {
	long long micro_time = Microsecond();
	lua_pushinteger(L, micro_time);
	return 1;
}

static int api_FunctionRegist(lua_State* L) {
	luaL_checktype(L, 1, LUA_TSTRING);
	luaL_checktype(L, 2, LUA_TFUNCTION);
	
	int top = lua_gettop(L);
	lua_newtable(L);
	lua_pushstring(L, "params");

	int count = top - 2;
	if (count > 0) {
		lua_pushstring(L, "plen");
		lua_pushinteger(L, count);
		lua_settable(L, -4);

		lua_newtable(L);
		for (int i = 1; i <= count; i++) {
			lua_pushinteger(L, i);
			lua_pushvalue(L, 3);
			lua_settable(L, -3);
			lua_remove(L, 3);
		}
	}
	else {
		lua_pushstring(L, "plen");
		lua_pushinteger(L, 0);
		lua_settable(L, -4);
		lua_pushnil(L);
	}
	lua_settable(L, -3);

	lua_pushstring(L, "function");
	lua_pushvalue(L, 2);
	lua_settable(L, -3);

	lua_getfield(L, LUA_REGISTRYINDEX, _FunctionCall);
	lua_pushvalue(L, 1);
	lua_pushvalue(L,-3);
	lua_settable(L, -3);
	lua_pop(L, 1);
	return 1;
}

static int api_FunctionRemove(lua_State* L) {
	luaL_checktype(L, 1, LUA_TSTRING);
	lua_getfield(L, LUA_REGISTRYINDEX, _FunctionCall);
	lua_pushvalue(L, 1);
	lua_pushnil(L);
	lua_settable(L, -3);
	return 0;
}

static int api_ReportOverMessage(lua_State* L){
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	char* buf = proto->stop_reason + UserErrorSize;
	int offset = 0;
	int argc = lua_gettop(L);
	std::map<const void*, bool> table_ref;
	for (int i = 1; i <= argc - 1; i++) {
		offset = obj_tostring(L, "", i, buf, UserErrorSize - 1, offset, table_ref);
		if (offset > UserErrorSize)
			break;
	}
	*(buf + offset) = 0x0;
	return 0;
}

static int api_HsocketConnect(lua_State* L){
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* ip = lua_tostring(L, 1);
	luaL_checktype(L, 2, LUA_TNUMBER);
	int port = (int)lua_tointeger(L, 2);
	luaL_checktype(L, 3, LUA_TNUMBER);
	PROTOCOL protocol = (PROTOCOL)lua_tointeger(L, 3);

	int ref, top = lua_gettop(L);
	if (top >= 4) {
		luaL_checktype(L, 4, LUA_TTABLE);
		ref = luaL_ref(L, LUA_REGISTRYINDEX);
	}

	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	HSOCKET hsock = SocketConnect((ReplayProtocol*)lua_topointer(L, -1), ip, port, protocol);
	if (hsock){
		if (top >= 4) {
			hsock->user_protocol = USER_PROTOCOL_HANDLE;
			hsock->user_val = ref;
		}
		lua_pushlightuserdata(L, hsock);
		return 1;
	}
	return 0;
}

static int api_HsocketSend(lua_State* L)
{
	int argc = lua_gettop(L);
	HSOCKET hsock = (HSOCKET)lua_touserdata(L, 1);
	luaL_argcheck(L, hsock != NULL, 1, "hsock parm error");
	if (hsock == NULL) return 0;
	size_t datalen = 0;
	luaL_checktype(L, 2, LUA_TSTRING);
	const char* data = lua_tolstring(L, 2, &datalen);
	if (argc > 2) {
		luaL_checktype(L, 3, LUA_TNUMBER);
		datalen = (int)lua_tointeger(L, 3);
	}
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	bool ret = SocketSend(proto, hsock, data, (int)datalen);
	lua_pushboolean(L, ret);
	return 1;
}

static int api_HsocketClose(lua_State* L)
{
	HSOCKET hsock = (HSOCKET)lua_touserdata(L, 1);
	luaL_argcheck(L, hsock != NULL, 1, "hsock parm error");
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);

	SocketClose(proto, hsock);

	int ref = int(hsock->user_val);
	if (ref == USER_PROTOCOL_HANDLE) {
		luaL_unref(L, LUA_REGISTRYINDEX, ref);
	}
	return 0;
}

static int api_PlayStop(lua_State* L)
{
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	char* buf = proto->stop_reason;
	int offset = 0;
	int argc = lua_gettop(L);
	std::map<const void*, bool> table_ref;
	for (int i = 1; i <= argc -1 ; i++) {
		offset = obj_tostring(L, "", i, buf, UserErrorSize - 1, offset, table_ref);
		if (offset > UserErrorSize)
			break;
	}
	*(buf + offset) = 0x0;
	proto->user_status.PlayStop = true;
	//PlayStop((ReplayProtocol*)lua_topointer(L, -1), lua_tostring(L, -2));
	return 0;
}

static int api_PlayNormal(lua_State* L)
{
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
#ifndef USER_PAUSE_COUNT
	proto->user_status.PlayMode = PLAY_NORMAL;
#else
	if (proto->user_pause_count > 0) proto->user_pause_count--;
	if (proto->user_pause_count == 0) proto->user_status.PlayPause = false;
#endif
	return 0;
}

static int api_PlayPause(lua_State* L)
{
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	proto->user_status.PlayPause = true;
#ifdef USER_PAUSE_COUNT
	proto->user_pause_count++;
#endif
	return 0;
}

static int api_PlayFast(lua_State* L)
{
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	bool fast = lua_toboolean(L, 1);
	proto->user_status.PlayFast = fast;
	return 0;
}

static int api_PlayStep(lua_State* L)
{
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	int step = proto->_fram_data.MsgPointer.index - 1;
	lua_pushinteger(L, step);
	return 1;
}

static int api_PlayStepSession(lua_State* L) {
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	int sessionid = PlayStepSession(proto);
	lua_pushinteger(L, sessionid);
	return 1;
}

static int api_PlayStepNote(lua_State* L) {
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	int index = proto->_fram_data.MsgPointer.index - 1;
	t_cache_message* msg = (*(proto->Task->messageList))[index];
	lua_pushlstring(L, msg->note, msg->note_len);
	return 1;
}

static int api_PlayBack(lua_State* L)
{
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	luaL_checktype(L, 1, LUA_TNUMBER);
	int step = (int)lua_tointeger(L, 1);
	PlayStepBack(proto, step);
	return 0;
}

static int api_PlayAction(lua_State* L)
{
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	luaL_checktype(L, 1, LUA_TNUMBER);
	PlayAction((ReplayProtocol*)lua_topointer(L, -1), (int)lua_tointeger(L, 1));
	return 1;
}

static int api_PlayNoStop(lua_State* L) {
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	proto->user_status.NotAutoStop = true;
	return 0;
}

static int api_PlayOver(lua_State* L) {
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	lua_pushboolean(L, PlayOver(proto));
	return 1;
}

static int api_ReportOnline(lua_State* L)
{
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	ReportOnline(proto);
	return 0;
}

static int api_ReportOffline(lua_State* L)
{
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	ReportOffline(proto);
	return 0;
}

static int api_ReportCounter(lua_State* L)
{
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* key = lua_tostring(L, 1);
	luaL_checktype(L, 2, LUA_TNUMBER);
	int value = (int)lua_tointeger(L, 2);
	luaL_checktype(L, 3, LUA_TNUMBER);
	int stype = (int)lua_tointeger(L, 3);
	luaL_checktype(L, 4, LUA_TNUMBER);
	int space_time = (int)lua_tointeger(L, 4);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	ReportCounter(proto, key, value, stype, space_time);
	return 0;
}

static int api_ReportAPIResponse(lua_State* L) {
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* api = lua_tostring(L, 1);
	luaL_checktype(L, 2, LUA_TNUMBER);
	int res_time = (int)lua_tointeger(L, 2);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	ReportApiResponse(proto, api, res_time);
	return 0;
}

static int api_ReportAPISend(lua_State* L) {
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* api = lua_tostring(L, 1);
	luaL_checktype(L, 2, LUA_TNUMBER);
	int send_flow = (int)lua_tointeger(L, 2);
	luaL_checktype(L, 3, LUA_TNUMBER);
	int send_count = (int)lua_tointeger(L, 3);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	ReportApiSend(proto, api, send_flow, send_count);
	return 0;
}

static int api_ReportAPIRecv(lua_State* L) {
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* api = lua_tostring(L, 1);
	luaL_checktype(L, 2, LUA_TNUMBER);
	int recv_flow = (int)lua_tointeger(L, 2);
	luaL_checktype(L, 3, LUA_TNUMBER);
	int recv_count = (int)lua_tointeger(L, 3);
	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
	ReportApiRecv(proto, api, recv_flow, recv_count);
	return 0;
}

//static int api_ReportMessage(lua_State* L)
//{
//	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
//	luaL_checktype(L, 1, LUA_TSTRING);
//	size_t size = 0;
//	const char* msg = lua_tolstring(L, 1, &size);
//	ReplayProtocol* proto = (ReplayProtocol*)lua_topointer(L, -1);
//	ReportMessage(proto, msg, size);
//	return 0;
//}

static int api_config_get_string_value(lua_State* L)
{
	const char* section = lua_tostring(L, 1);
	luaL_checktype(L, 2, LUA_TSTRING);
	const char* key = lua_tostring(L, 2);
	luaL_checktype(L, 3, LUA_TSTRING);
	const char* def = lua_tostring(L, 3);
	const char* value = config_get_string_value(section, key, def);
	lua_pushstring(L, value);
	return 1;
}

static int api_config_get_number_value(lua_State* L)
{
	const char* section = lua_tostring(L, 1);
	luaL_checktype(L, 2, LUA_TSTRING);
	const char* key = lua_tostring(L, 2);
	luaL_checktype(L, 3, LUA_TNUMBER);
	int def = (int)lua_tointeger(L, 3);
	int value = config_get_int_value(section, key, def);
	lua_pushinteger(L, value);
	return 1;
}

static int api_config_get_bool_value(lua_State* L)
{
	const char* section = lua_tostring(L, 1);
	luaL_checktype(L, 2, LUA_TSTRING);
	const char* key = lua_tostring(L, 2);
	luaL_checktype(L, 3, LUA_TBOOLEAN);
	bool def = lua_toboolean(L, 3);
	bool value = config_get_bool_value(section, key, def);
	lua_pushboolean(L, value);
	return 1;
}

static int api_SocketPeerAddrGet(lua_State* L)
{
	HSOCKET hsock = (HSOCKET)lua_touserdata(L, 1);
	luaL_argcheck(L, hsock != NULL, 1, "hsock parm error");

	char ip[40] = { 0x0 };
	int port = 0;
	HsocketPeerAddr(hsock, ip, sizeof(ip), &port);
	lua_pushstring(L, ip);
	lua_pushinteger(L, port);
	return 2;
}

static int api_SocketLocalAddrGet(lua_State* L)
{
	HSOCKET hsock = (HSOCKET)lua_touserdata(L, 1);
	luaL_argcheck(L, hsock != NULL, 1, "hsock parm error");

	char ip[40] = { 0x0 };
	int port = 0;
	HsocketLocalAddr(hsock, ip, sizeof(ip), &port);
	lua_pushstring(L, ip);
	lua_pushinteger(L, port);
	return 2;
}

static int api_SocketPeerAddrSet(lua_State* L)
{
	HSOCKET hsock = (HSOCKET)lua_touserdata(L, 1);
	luaL_argcheck(L, hsock != NULL, 1, "hsock parm error");
	if (hsock == NULL) return 0;
	luaL_checktype(L, 2, LUA_TSTRING);
	const char* ip = lua_tostring(L, 2);
	luaL_checktype(L, 3, LUA_TNUMBER);
	int port = (int)lua_tointeger(L, 3);

	HsocketPeerAddrSet(hsock, ip, port);
	return 0;
}

static int api_GetHostByName(lua_State* L) {
	const char* name = lua_tostring(L, 1);
	char ip[40] = { 0x0 };
	GetHostByName(name, ip, sizeof(ip));
	lua_pushstring(L, ip);
	return 1;
}

static void http_response_callback(HttpTask* task, BaseWorker* worker, HttpResponse* res, int ret_state) {
	int state;
	LuaProtocol* proto = (LuaProtocol*)worker;
	lua_State* L = proto->PL;
	int ref = (int)task->user_number;
	lua_rawgeti(L, LUA_REGISTRYINDEX, ref);

	if (ret_state == State_OK) {
		lua_newtable(L);

		lua_pushstring(L, "version");
		lua_pushstring(L, res->version.c_str());
		lua_settable(L, -3);

		lua_pushstring(L, "state_code");
		lua_pushinteger(L, res->state_code);
		lua_settable(L, -3);

		lua_pushstring(L, "state");
		lua_pushstring(L, res->state.c_str());
		lua_settable(L, -3);

		lua_pushstring(L, "header");
		lua_newtable(L);
		HttpHeader::iterator iter;
		for (iter = res->header.begin(); iter != res->header.end(); ++iter) {
			lua_pushstring(L, iter->first.c_str());
			lua_pushstring(L, iter->second.c_str());
			lua_settable(L, -3);
		}
		lua_settable(L, -3);

		lua_pushstring(L, "content");
		lua_pushstring(L, res->content.c_str());
		lua_settable(L, -3);

		lua_pushnil(L);
	}
	else {
		lua_pushnil(L);
		if (ret_state == State_Connection_Faile) lua_pushstring(L, "connection close");
		else if (ret_state == State_Connection_Faile) lua_pushstring(L, "connection faile");
	}
	state = lua_pcall(L, 2, 0, 0);
	if (state != LUA_OK) {
		sheeps_report(L, state, proto, false, __func__, __LINE__);
	}
	luaL_unref(L, LUA_REGISTRYINDEX, ref);
}

static int api_HttpRequest(lua_State* L) {
	luaL_checktype(L, 1, LUA_TSTRING);
	luaL_checktype(L, 2, LUA_TSTRING);
	const char* method = lua_tostring(L, 1);
	const char* url = lua_tostring(L, 2);
	HttpHeader	header;
	const char* content = NULL;
	int type = lua_type(L, 3);
	if (type == LUA_TTABLE) {
		lua_pushnil(L);
		lua_copy(L, 3, -1);
		lua_pushnil(L);
		while (lua_next(L, -2) != 0) {
			const char* key = lua_tostring(L, -2);
			const char* val = lua_tostring(L, -1);
			header[key] = val;
			lua_pop(L, 1);
		}
		lua_pop(L, 1);
	}
	type = lua_type(L, 4);
	if (type == LUA_TTABLE) {
		// HttpHeader::iterator iter = header.find("Content-Type");
		// if (iter != header.end() && iter->second == "application/x-www-form-urlencoded") {

		// }
		// else {
		// 	header["Content-Type"] = "application/json";
		// }
		// cJSON* root = cJSON_CreateObject();
		// lua_pushnil(L);
		// lua_copy(L, 4, -1);
		// lua_pushnil(L);
		// while (lua_next(L, -2) != 0) {
		// 	const char* key = lua_tostring(L, -2);
		// 	const char* val = lua_tostring(L, -1);
		// 	lua_pop(L, 1);
		// }
		// lua_pop(L, 1);
	}
	else if (type == LUA_TSTRING) {
		content = lua_tostring(L, 4);
	}
	luaL_checktype(L, 5, LUA_TFUNCTION);
	int ref = luaL_ref(L, LUA_REGISTRYINDEX);
	lua_getfield(L, LUA_REGISTRYINDEX, _UserProtoName);
	LuaProtocol* proto = (LuaProtocol*)lua_topointer(L, -1);
	HttpTask* task = HttpClient->request(proto, method, url, &header, content, http_response_callback);
	if (task) {
		task->user_number = ref;
		lua_pushboolean(L, 1);
		return 1;
	}
	lua_pushboolean(L, 0);
	return 1;
}

#ifdef KCP_SUPPORT
static int api_kcp_create(lua_State* L) {
	if (KCP_PROTOCOL) {
		int argc = lua_gettop(L);
		int mode = 0;
		HSOCKET hsock = (HSOCKET)lua_topointer(L, 1);
		luaL_argcheck(L, hsock != NULL, 1, "hsock parm error");
		luaL_checktype(L, 2, LUA_TNUMBER);
		int conv = (int)lua_tointeger(L, 2);
		if (argc > 2) {
			luaL_checktype(L, 3, LUA_TNUMBER);
			mode = (int)lua_tointeger(L, 3);
		}
		int ret = HsocketKcpCreate(hsock, conv, mode);
		if (ret)
			lua_pushboolean(L, 0);
		else
			lua_pushboolean(L, 1);
	}
	else {
		lua_pushboolean(L, 0);
	}
	return 1;
}

static int api_kcp_nodelay(lua_State* L) {
	HSOCKET hsock = (HSOCKET)lua_topointer(L, 1);
	luaL_argcheck(L, hsock != NULL, 1, "hsock parm error");
	luaL_checktype(L, 2, LUA_TNUMBER);
	int nodelay = (int)lua_tointeger(L, 2);
	luaL_checktype(L, 3, LUA_TNUMBER);
	int interval = (int)lua_tointeger(L, 3);
	luaL_checktype(L, 4, LUA_TNUMBER);
	int resend = (int)lua_tointeger(L, 4);
	luaL_checktype(L, 5, LUA_TNUMBER);
	int nc = (int)lua_tointeger(L, 5);
	HsocketKcpNodelay(hsock, nodelay, interval, resend, nc);
	return 0;
}

static int api_kcp_wndsize(lua_State* L) {
	HSOCKET hsock = (HSOCKET)lua_topointer(L, 1);
	luaL_argcheck(L, hsock != NULL, 1, "hsock parm error");
	luaL_checktype(L, 2, LUA_TNUMBER);
	int snwnd = (int)lua_tointeger(L, 2);
	luaL_checktype(L, 3, LUA_TNUMBER);
	int rvwnd = (int)lua_tointeger(L, 3);
	HsocketKcpWndsize(hsock, snwnd, rvwnd);
	return 0;
}

//static int api_kcp_getconv(lua_State* L) {
//	HSOCKET hsock = (HSOCKET)lua_topointer(L, 1);
//	luaL_argcheck(L, hsock != NULL, 1, "hsock parm error");
//	int conv = HsocketKcpGetconv(hsock);
//	lua_pushinteger(L, conv);
//	return 1;
//}

static int api_kcp_debug(lua_State* L) {
	HSOCKET hsock = (HSOCKET)lua_topointer(L, 1);
	luaL_argcheck(L, hsock != NULL, 1, "hsock parm error");
	char buf[256] = { 0x0 };
	int n = HsocketKcpDebug(hsock, buf, sizeof(buf));
	lua_pushlstring(L, buf, n);
	return 1;
}
#endif

int register_sheeps_api(lua_State* L)
{
	lua_register(L, "Log", api_Log);
	lua_register(L, "Microsecond", api_Microsecond);
	lua_register(L, "FunctionRegist", api_FunctionRegist);
	lua_register(L, "FunctionRemove", api_FunctionRemove);
	lua_register(L, "ConfigGetString", api_config_get_string_value);
	lua_register(L, "ConfigGetNumber", api_config_get_number_value);
	lua_register(L, "ConfigGetBool", api_config_get_bool_value);

	lua_register(L, "SocketConnect", api_HsocketConnect);
	lua_register(L, "SocketSend", api_HsocketSend);
	lua_register(L, "SocketClose", api_HsocketClose);
	lua_register(L, "SocketPeerAddrGet", api_SocketPeerAddrGet);
	lua_register(L, "SocketLocalAddrGet", api_SocketLocalAddrGet);
	lua_register(L, "SocketPeerAddrSet", api_SocketPeerAddrSet);
	lua_register(L, "GetHostByName", api_GetHostByName);
	lua_register(L, "HttpRequest", api_HttpRequest);
#ifdef KCP_SUPPORT
	lua_register(L, "KcpCreate", api_kcp_create);
	lua_register(L, "KcpNodelay", api_kcp_nodelay);
	lua_register(L, "KcpWndsize", api_kcp_wndsize);
	//lua_register(L, "KcpGetconv", api_kcp_getconv);
	lua_register(L, "KcpDebug", api_kcp_debug);
#endif

	lua_register(L, "PlayStop", api_PlayStop);
	lua_register(L, "PlayNormal", api_PlayNormal);
	lua_register(L, "PlayPause", api_PlayPause);
	lua_register(L, "PlayFast", api_PlayFast);
	lua_register(L, "PlayStep", api_PlayStep);
	lua_register(L, "PlayStepSession", api_PlayStepSession);
	lua_register(L, "PlayStepNote", api_PlayStepNote);
	lua_register(L, "PlayBack", api_PlayBack);
	lua_register(L, "PlayAction", api_PlayAction);
	lua_register(L, "PlayNoStop", api_PlayNoStop);
	lua_register(L, "PlayOver", api_PlayOver);

	lua_register(L, "ReportOnline", api_ReportOnline);
	lua_register(L, "ReportOffline", api_ReportOffline);
	lua_register(L, "ReportCounter", api_ReportCounter);
	lua_register(L, "ReportAPIResponse", api_ReportAPIResponse);
	lua_register(L, "ReportAPISend", api_ReportAPISend);
	lua_register(L, "ReportAPIRecv", api_ReportAPIRecv);
	//lua_register(L, "ReportMessage", api_ReportMessage);
	lua_register(L, "ReportOverMessage", api_ReportOverMessage);
	return 0;
}//  31 api count
